#pragma once
#include "neighbor_list.h"
#include <memory>
#include <vector>
#include <Utilities/glm_ext.h>
#include <DataCore/buffer_data.h>

namespace PhysLeo {

/**
 * cpu version of space hash.
 * automatically detect the bouding box of a point set, output the neighbor list accoding to the min and max neighbor radius.
 * If there are more than SPACE_HASH_NEIGHBOR_MAX points in a point's neighbor list, it will only store the closest.
 * warning: it should be noticed, when the point set are distributed unevenly, you may get a incorrect result by this method. Because points may overflow in one grid.
 * however in our discretization, point set will be distributed evenly.
 */
template<typename T>
class SpaceHashCpu
{
public:
    SpaceHashCpu();
    virtual ~SpaceHashCpu();

    virtual void setSpace(T resolution, glm::tvec3<T> low, glm::tvec3<T> high, int np_max);
    virtual void constructHashTable(std::shared_ptr<BufferData<glm::tvec3<T>>> ptr_pos);

    virtual void queryNeighbor(std::shared_ptr<BufferData<glm::tvec3<T>>> ptr_pos, std::shared_ptr<BufferData<NeighborList>> ptr_neighbor_list, T max_radius, T min_radius);
    virtual std::vector<glm::tvec3<T>> bbox(std::shared_ptr<BufferData<glm::tvec3<T>>> ptr_pos);

protected:
    /**
     * the total number of grid
     */
    int num_;

    /**
     * x dimension of grid 
     */
    int nx_;

    /**
     * y dimension of grid
     */
    int ny_;

    /**
     * z dimension of grid
     */
    int nz_;

    /**
     * resolution of grid, also the length of grid
     */
    T resolution_;

    /**
     * the min corner of bounding box
     */
    glm::tvec3<T> low_;

    /**
     * the max corner of bounding box
     */
    glm::tvec3<T> high_;

    /**
     * maximum particle number for each grid, it should be large enough to contain all the particles in any grid.
     * if not, neighbor quering may be incorrect.  it need to be set as 2 times of average number of points in one grid
     */
    int np_max_;

    /**
     * id of point located in a grid
     */
    std::shared_ptr<BufferData<int>> ids_;

    /**
     * number of points in a grid
     */
    std::shared_ptr<BufferData<int>> counter_;
};

}